import threading

from util.decorators import *
import gobject
# TODO: Note the Queue interface.
try:
    from async import chunk
except:
    print 'Couldnt load chunk'
    pass

import logging
logger = logging.getLogger('network.queues')

class Queue:
    lock = threading.Lock()

    def __init__(self):
        self.__queue = []

    @synchronized(lock)
    def put(self, data, addr=(None, None)):
        self.__queue.append((data, addr))

    def next(self):
        if len(self.__queue) == 0:
            return (None, (None, None))
        else:
            return self.__queue.pop(0)

    def __len__(self):
        return len(self.__queue)

class MessageQueue(gobject.GObject):

    __gsignals__ = {
        'busy' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                (gobject.TYPE_BOOLEAN,)),
    }

    lock = threading.Lock()

    def __init__(self):
        gobject.GObject.__init__(self)
        self.__queue = []
        self.__busy = False

    @synchronized(lock)
    def put(self, msg, addr, seqs = []):
        p = MessageQueue.PendingSend(msg, addr, seqs)
        if p not in self.__queue:
            logger.debug('Putting message %s in queue', msg.header.mid)
            self.__queue.append(p)

    @synchronized(lock)
    def next(self):
        if len(self.__queue) == 0:  
            next = (None, (None, None))
        else:
            if not self.__busy:
                self.__busy = True
                self.emit('busy', True)
            c = self.__queue[0].next()
            next = (c.raw(), self.__queue[0].addr)
            if isinstance(c, chunk.EndChunk):
                self.__queue.pop(0)
                self.__busy = False
                self.emit('busy', False)
        return next

    def __len__(self):
        return len(self.__queue)

    class PendingSend:
        
        def __init__(self, msg, addr, seqs):
            self.msg = msg
            self.addr = addr
            self.seqs = seqs
            self.__i = -1
            logger.debug('Putting a PendingSend: msg_id %s, target addr %s, seqs, %s', msg.header.mid, addr, seqs)

        def next(self):
            if self.seqs:  
                self.__i += 1
                if self.__i > len(self.seqs):
                    raise StopIteration
                elif self.__i == len(self.seqs):
                    return chunk.EndChunk(self.msg.header.mid, -1)
                else:
                    return self.msg.get_chunk(self.seqs[self.__i])
            else:
                try:
                    return self.msg.next()
                except StopIteration:
                    return chunk.EndChunk(self.msg.header.mid, -1)

        def __eq__(self, other):
            return (self.msg.header.mid == other.msg.header.mid
                    and self.addr == other.addr
                    and self.seqs == other.seqs)

        def __hash__(self):
            return hash(self.msg.header.mid)
gobject.type_register(MessageQueue)
